package pl.edu.agh.ssm.monitor.listeners;

import java.util.LinkedList;

import jlibrtp.RtcpPkt;
import jlibrtp.RtcpPktBYE;
import jlibrtp.RtcpPktRR;
import jlibrtp.RtcpPktSDES;
import jlibrtp.RtcpPktSR;
import pl.edu.agh.ssm.monitor.data.ReceiverReportInfo;
import pl.edu.agh.ssm.monitor.data.SenderReportInfo;
import pl.edu.agh.ssm.monitor.data.SessionConnection;
import pl.edu.agh.ssm.monitor.data.Packet;
import pl.edu.agh.ssm.monitor.data.Session;
import pl.edu.agh.ssm.monitor.data.SessionMedia;
import pl.edu.agh.ssm.monitor.data.SessionMediaType;
import pl.edu.agh.ssm.monitor.data.SessionNode;
import pl.edu.agh.ssm.monitor.data.SessionNodeDescription;
import pl.edu.agh.ssm.monitor.data.SessionNodeType;
import pl.edu.agh.ssm.monitor.parsers.rtcp.RTCPParser;

/**
 * Manages session participants, connections between them, inform session
 * container about session expire,
 * 
 * @author Tomasz Jadczyk
 * 
 */
public class RTCPDataListener implements PacketListener {

	@SuppressWarnings("unused")
	private PacketListener sessionListener = null;
	private Session session = null;
	private int priority;
	private RTCPParser packetsParser = new RTCPParser();

	public RTCPDataListener(PacketListener sessionListener, Session session) {
		this.sessionListener = sessionListener;
		this.session = session;
	}

	public RTCPDataListener(Session session) {
		super();
		this.session = session;
	}

	@Override
	public int getPriority() {
		return priority;
	}

	@Override
	public void processPacket(Packet packet) {
		if (packet.getData()[1] != 204) { // do not parse Application Specific
											// Report
//			System.out.println("Process packet: FROM"
//					+ packet.getSourceAddress().getHostAddress() + ":"
//					+ packet.getSourcePort() + " TO: "
//					+ packet.getDestAddress().getHostAddress() + ":"
//					+ packet.getDestPort());
			try {
				LinkedList<RtcpPkt> rtcpPackets = packetsParser.parse(packet
						.getData());

				SessionNode node = null;
				boolean newNode = false;
				if (!rtcpPackets.isEmpty()) {
					// get node by ssrc
					node = session.getNode(rtcpPackets.getFirst().getSsrc());
					if (node == null) { // new node for this session
						node = new SessionNode(packet.getSourceAddress(),
								session.getSessionID());
						node.setSsrc(rtcpPackets.getFirst().getSsrc());
						newNode = true;
					}
				}

				for (RtcpPkt rtcpPacket : rtcpPackets) {
					/* Sender report */
					if (rtcpPacket instanceof RtcpPktSR) {
						RtcpPktSR sReport = (RtcpPktSR) rtcpPacket;
						processReport(sReport, packet, node, newNode);
					}

					/* Receiver report */
					if (rtcpPacket instanceof RtcpPktRR) {
						RtcpPktRR rReport = (RtcpPktRR) rtcpPacket;
						processReport(rReport, packet, node, newNode);
					}

					/* Source Description report */
					if (rtcpPacket instanceof RtcpPktSDES) {
						RtcpPktSDES sdesReport = (RtcpPktSDES) rtcpPacket;
						processReport(sdesReport, packet, node, newNode);
					}

					/* Bye packet */
					if (rtcpPacket instanceof RtcpPktBYE) {
						RtcpPktBYE byeReport = (RtcpPktBYE) rtcpPacket;
						processReport(byeReport, packet, node, newNode);
					}

					/* Other report */
					// Currently are not supported
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	private void processReport(RtcpPktRR receiverReport, Packet packet,
			SessionNode node, boolean isNewNode) {
//		System.out.println("RR method");
		SessionNode receiver = session.getNode(receiverReport.getSsrc());
		if (receiver == null) {
			receiver = new SessionNode(packet.getSourceAddress(), session
					.getSessionID());
			receiver.setSsrc(receiverReport.getSsrc());
			receiver.setNodeType(SessionNodeType.RECEIVER);
			receiver.setNodeDescription(new SessionNodeDescription());
//			System.out.println("RTP Listener: " + this);
//			System.out.println("RTP: Create new node from packet: Source:"
//					+ packet.getSourceAddress().getHostAddress() + ":"
//					+ packet.getSourcePort() + ", Dest: "
//					+ packet.getDestAddress().getHostAddress() + ":"
//					+ packet.getDestPort());
			session.addNode(receiver);
		}

		receiver.setLastReceiverInfoTime(System.currentTimeMillis());

		for (int i = 0; i < receiverReport.getItemCount(); i++) {
			long ssrc = receiverReport.getReporteeSsrc()[i];
			// check if connection was set up
			SessionNode source = session.getNode(ssrc);
			if (source != null
					&& (source.getNodeType() == SessionNodeType.SENDER || source
							.getNodeType() == SessionNodeType.SENDER_AND_RECEIVER)
					&& source != receiver) {
				SessionConnection cn = null;
				if (packet.getDestAddress().isMulticastAddress()) {
					cn = session.getConnection(source, receiver, packet
							.getDestAddress(), packet.getDestPort() - 1);
				} else {
					cn = session.getConnection(source, receiver, packet
							.getSourceAddress(), packet.getSourcePort() - 1);
				}
				if (cn == null) { // connection was not found
					cn = new SessionConnection(source, receiver, session
							.getId());
					/* Set Connection informations, address and RTP data port */
					if (packet.getDestAddress().isMulticastAddress()) {
						cn.setConnectionAddress(packet.getDestAddress());
						cn.setConnectionPort(packet.getDestPort() - 1);
					} else {
						cn.setConnectionAddress(packet.getSourceAddress());
						cn.setConnectionPort(packet.getSourcePort() - 1);
					}

					/* Unknown media - it is filled by RTPListener */
					SessionMedia media = new SessionMedia();
					media.setMediaType(SessionMediaType.OTHER);
					media.setMediaName("Unknown");
					cn.setMediaDesc(media);
					System.out.println("New connection found, from: "
							+ source.getSsrc() + ", to: " + receiver.getSsrc());
					session.addConnection(cn);
				}
				/* -- Prepare receiver report informations */
				ReceiverReportInfo reportInfo = new ReceiverReportInfo();
				reportInfo.setJitter(receiverReport.getInterArvJitter()[i]);
				reportInfo.setPackagesLostFraction(receiverReport
						.getLossFraction()[i]);
				reportInfo.setPackagesLostTotal(receiverReport
						.getLostPktCount()[i]);
				reportInfo.setLastPackageReceivedStamp(receiverReport
						.getExtHighSeqRecv()[i]);
				reportInfo.setLastSRRecvNTPTimeStamp(receiverReport
						.getTimeStampLSR()[i]);
				reportInfo
						.setTimeFromLastSRRecv(receiverReport.getDelaySR()[i]);

				cn.setMediaReport(reportInfo);
				cn.setLastReceiverReportTime(System.currentTimeMillis());
				session.updateConnection(cn);
			}
		}
		// if(receiver.getNodeType() == SessionNodeType.SENDER) {
		// receiver.setNodeType(SessionNodeType.SENDER_AND_RECEIVER);
		// }

		receiver.setReport(receiverReport);
		receiver.setNodeType(SessionNodeType.RECEIVER);
		session.updateNode(receiver);
	}

	private void processReport(RtcpPktSR senderReport, Packet packet,
			SessionNode node, boolean isNewNode) {
//		System.out.println("SR Method");
		if (isNewNode) {
			node.setSsrc(senderReport.getSsrc());
			// node.setNodeType(SessionNodeType.SENDER);
			node.setNodeDescription(new SessionNodeDescription());
			session.addNode(node);
			// } else {
			// if(node.getNodeType() == SessionNodeType.RECEIVER) {
			// node.setNodeType(SessionNodeType.SENDER_AND_RECEIVER);
			// }
		}

		SenderReportInfo reportInfo = new SenderReportInfo();
		reportInfo.setNtp1TimeStamp(senderReport.getNtpTs1());
		reportInfo.setNtp2TimeStamp(senderReport.getNtpTs2());
		reportInfo.setRtpTimeStamp(senderReport.getRtpTs());
		reportInfo.setTotalPackets(senderReport.getSendersPktCount());
		reportInfo.setTotalOctets(senderReport.getSendersOctCount());

		node.setSenderInfo(reportInfo);
		node.setLastSenderInfoTime(System.currentTimeMillis());

		node.setReport(senderReport);
		node.setNodeType(SessionNodeType.SENDER);
		session.updateNode(node);

	}

	private void processReport(RtcpPktSDES sourceDescReport, Packet packet,
			SessionNode node, boolean isNewNode) {
//		System.out.println("SDES method used");
		for (int i = 0; i < sourceDescReport.getItemCount(); i++) {
			SessionNode sessionNode = session.getNode(sourceDescReport
					.getSsrcs()[i]);
			boolean isNew = false;
			if (sessionNode == null) {
				sessionNode = new SessionNode(packet.getDestAddress(), session
						.getId());
				sessionNode.setSsrc(sourceDescReport.getSsrcs()[i]);
				sessionNode.setNodeType(SessionNodeType.RECEIVER);
				sessionNode.setNodeDescription(new SessionNodeDescription());
				isNew = true;
			}
			SessionNodeDescription description = sessionNode
					.getNodeDescription();

			if (sourceDescReport.getCnames()[i] != null) {
				description.setCname(sourceDescReport.getCnames()[i]);
			}
			if (sourceDescReport.getEmails()[i] != null) {
				description.setEmail(sourceDescReport.getEmails()[i]);
			}
			if (sourceDescReport.getLocs()[i] != null) {
				description.setLoc(sourceDescReport.getLocs()[i]);
			}
			if (sourceDescReport.getNames()[i] != null) {
				description.setName(sourceDescReport.getNames()[i]);
			}
			if (sourceDescReport.getNotes()[i] != null) {
				description.setNote(sourceDescReport.getNotes()[i]);
			}
			if (sourceDescReport.getPhones()[i] != null) {
				description.setPhone(sourceDescReport.getPhones()[i]);
			}
			if (sourceDescReport.getPrivs()[i] != null) {
				description.setPriv(sourceDescReport.getPrivs()[i]);
			}
			if (sourceDescReport.getTools()[i] != null) {
				description.setTool(sourceDescReport.getTools()[i]);
			}

			sessionNode.setNodeDescription(description);

			if (isNew) {
				session.addNode(sessionNode);
			} else {
				session.updateNode(sessionNode);
			}
		}

	}

	private void processReport(RtcpPktBYE byeReport, Packet packet,
			SessionNode node, boolean isNewNode) {
//		System.out.println("BYE method used");

		session.removeNode(node);

	}

	// private void processReport(RtcpPkt otherReport,
	// Packet packet, SessionNode node, boolean isNewNode) {
	// System.out.ln("Rtcp method used");
	// }

	@Override
	public void setPriority(int priority) {
		this.priority = priority;
	}

	// public void testMethod(RtcpPkt packet) {
	// if (packet instanceof RtcpPktSR) {
	// RtcpPktSR sReport = (RtcpPktSR) packet;
	// processReport(sReport);
	// System.out.ln("Sender report found");
	// } else {
	// processReport(packet);
	// }
	// }

}
